"""
Redis工具类

功能：
    - 字符串操作（set, get, incr, decr）
    - Hash操作（hset, hget, hgetall）
    - List操作（lpush, rpush, lpop, rpop, lrange）
    - ZSet操作（zadd, zrem, zrangebyscore）
    - 分布式锁（acquire_lock, release_lock）
    
使用场景：
    - 队列管理（异步任务、削峰、解耦）
    - 缓存管理
    - 会话存储
    - 计数器
    - 分布式锁（防止缓存击穿、控制并发）
"""
import uuid
from typing import Optional
from redis import Redis, ConnectionPool
from config.settings import settings


class Mredis:
    """Redis操作工具类"""
    
    def __init__(self, db=0):
        """
        初始化Redis连接池
        
        参数:
            db (int): Redis数据库编号（0-15）
        """
        self.pool = ConnectionPool(
            host=settings.REDIS_HOST,
            port=settings.REDIS_PORT,
            db=db,
            decode_responses=False  # 不自动解码，手动处理
        )
        self.r = Redis(connection_pool=self.pool)

    # ============ 字符串操作 ============
    
    def set_str(self, key, value):
        """
        设置字符串
        
        参数:
            key: 键
            value: 值
        """
        return self.r.set(key, value)

    def setex_str(self, key, times, value):
        """
        设置字符串（带过期时间）
        
        参数:
            key: 键
            times: 过期时间（秒）
            value: 值
        """
        return self.r.setex(key, times, value)

    def setnx_str(self, key, value):
        """
        设置字符串（仅当key不存在时）
        
        参数:
            key: 键
            value: 值
            
        返回:
            bool: 设置成功返回True，key已存在返回False
        """
        return self.r.setnx(key, value)

    def get_str(self, key):
        """
        获取字符串
        
        参数:
            key: 键
            
        返回:
            str: 值，不存在返回None
        """
        value = self.r.get(key)
        if value:
            value = value.decode()
        return value

    def incr_str(self, key, count=1):
        """
        自增
        
        参数:
            key: 键
            count: 增量（默认1）
            
        返回:
            int: 自增后的值
        """
        return self.r.incr(key, count)

    def decr_str(self, key, count=1):
        """
        自减
        
        参数:
            key: 键
            count: 减量（默认1）
            
        返回:
            int: 自减后的值
        """
        return self.r.decr(key, count)

    def str_del(self, key):
        """
        删除字符串
        
        参数:
            key: 键
        """
        return self.r.delete(key)

    # ============ Hash操作 ============
    
    def hash_add(self, key, field, value):
        """
        添加Hash字段
        
        参数:
            key: Hash名称
            field: 字段名
            value: 字段值
        """
        return self.r.hset(key, field, value)

    def hash_getall(self, key):
        """
        获取Hash的所有字段
        
        参数:
            key: Hash名称
            
        返回:
            dict: 所有字段和值
        """
        return self.r.hgetall(key)

    def hash_getone(self, key, field):
        """
        获取Hash的某个字段
        
        参数:
            key: Hash名称
            field: 字段名
            
        返回:
            bytes: 字段值
        """
        return self.r.hget(key, field)

    def hash_hdel(self, key, field):
        """
        删除Hash的某个字段
        
        参数:
            key: Hash名称
            field: 字段名
        """
        return self.r.hdel(key, field)

    # ============ List操作（队列）============
    
    def list_rpush(self, key, value):
        """
        从右侧push到列表（入队）
        
        参数:
            key: 列表名称
            value: 值
            
        使用场景：
            - 任务队列
            - 消息队列
        """
        return self.r.rpush(key, value)
    
    def list_lpush(self, key, value):
        """
        从左侧push到列表
        
        参数:
            key: 列表名称
            value: 值
        """
        return self.r.lpush(key, value)

    def list_lpop(self, key):
        """
        从左侧pop列表（出队）
        
        参数:
            key: 列表名称
            
        返回:
            bytes: 值
        """
        return self.r.lpop(key)
    
    def list_rpop(self, key):
        """
        从右侧pop列表
        
        参数:
            key: 列表名称
            
        返回:
            bytes: 值
        """
        return self.r.rpop(key)

    def list_lrange(self, key, min, max):
        """
        获取列表范围内的元素
        
        参数:
            key: 列表名称
            min: 起始索引
            max: 结束索引
            
        返回:
            list: 元素列表
        """
        return self.r.lrange(key, min, max)

    def list_len(self, key):
        """
        获取列表长度
        
        参数:
            key: 列表名称
            
        返回:
            int: 列表长度
        """
        return self.r.llen(key)

    def list_del(self, key, field):
        """
        删除列表中的元素
        
        参数:
            key: 列表名称
            field: 要删除的值
        """
        return self.r.lrem(key, -1, field)

    def list_rpoplpush(self, list1, list2):
        """
        从list1右侧pop，并push到list2左侧
        
        参数:
            list1: 源列表
            list2: 目标列表
            
        使用场景：
            - 任务转移
            - 备份队列
        """
        return self.r.rpoplpush(list1, list2)

    # ============ ZSet操作（有序集合）============
    
    def zset_zadd(self, key, score, value):
        """
        添加有序集合元素
        
        参数:
            key: 有序集合名称
            score: 分数
            value: 值
            
        使用场景：
            - 排行榜
            - 延时队列（按时间戳排序）
        """
        map_data = {value: score}
        return self.r.zadd(key, mapping=map_data)

    def zset_zrem(self, key, value):
        """
        删除有序集合元素
        
        参数:
            key: 有序集合名称
            value: 值
        """
        return self.r.zrem(key, value)

    def zset_zrangebyscore(self, key, min, max):
        """
        按分数范围获取有序集合元素
        
        参数:
            key: 有序集合名称
            min: 最小分数
            max: 最大分数
            
        返回:
            list: 元素列表
        """
        return self.r.zrangebyscore(key, min, max)

    # ============ 通用操作 ============
    
    def get_keys(self, pattern):
        """
        模糊查询key
        
        参数:
            pattern: 匹配模式（支持*通配符）
            
        返回:
            list: key列表
            
        示例:
            get_keys("user:*")  # 查询所有user开头的key
        """
        return self.r.keys(pattern)
    
    def exists(self, key):
        """
        检查key是否存在
        
        参数:
            key: 键
            
        返回:
            bool: 存在返回True
        """
        return self.r.exists(key)
    
    def expire(self, key, seconds):
        """
        设置key的过期时间
        
        参数:
            key: 键
            seconds: 过期时间（秒）
        """
        return self.r.expire(key, seconds)
    
    def ttl(self, key):
        """
        获取key的剩余过期时间
        
        参数:
            key: 键
            
        返回:
            int: 剩余秒数，-1表示永不过期，-2表示key不存在
        """
        return self.r.ttl(key)

    # ============ 分布式锁操作 ============
    
    def acquire_lock(self, lock_key: str, expire_seconds: int = 10) -> Optional[str]:
        """
        获取分布式锁（基于SETNX）
        
        参数:
            lock_key: 锁的key
            expire_seconds: 锁过期时间（秒，防止死锁）
            
        返回:
            str: 锁的唯一标识（用于释放锁时校验），None表示获取失败
            
        使用场景:
            - 防止缓存击穿（多个请求同时查库）
            - 控制并发访问（如限流）
            - 保证原子性操作
        """
        # 生成唯一标识（避免释放其他线程的锁）
        lock_value = str(uuid.uuid4())
        # SETNX + 过期时间（原子操作）：key不存在则设置，同时指定过期时间
        result = self.r.set(
            name=lock_key,
            value=lock_value,
            nx=True,  # SETNX
            ex=expire_seconds  # 过期时间
        )
        return lock_value if result else None
    
    def release_lock(self, lock_key: str, lock_value: str) -> bool:
        """
        释放分布式锁（原子性校验并删除）
        
        参数:
            lock_key: 锁的key
            lock_value: 加锁时的唯一标识
            
        返回:
            bool: 释放成功返回True，否则False
            
        注意:
            使用Lua脚本保证原子性，防止释放其他线程的锁
        """
        # Lua脚本：先校验锁的value是否匹配，匹配则删除（保证原子性）
        lua_script = """
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end
        """
        # 执行脚本：KEYS[1]为lock_key，ARGV[1]为lock_value
        result = self.r.eval(lua_script, 1, lock_key, lock_value)
        return result == 1


# 创建全局Redis实例（默认使用数据库0）
r = Mredis(db=0)

